#include "socketx.h"

int socketx_init()
{
	signal(SIGPIPE, SIG_IGN);
	return 0;
}

int socketx_unit()
{
	return 0;
}

int socketx_create(int iDomain, int iType, int iProtocol)
{
	int sock = socket(iDomain, iType, iProtocol);
	if (sock == -1)
		return -1;
    
    // 设置为地址可重用
	int opt = 1;
	if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(int)) != 0)
		return -1;
    
    // 设置套接口为非阻塞
	int iSave = fcntl(sock, F_GETFL);
	fcntl(sock, F_SETFL, iSave | O_NONBLOCK);
    
	return sock;
}

int socketx_close(int sock)
{
	return close(sock);
}

int socketx_bind(int sock, const sockaddr *pInfo, socklen_t len)
{
	return bind(sock, pInfo, len);
}

int socketx_listen(int sock, int iBacklog)
{
	return listen(sock, iBacklog);
}

int socketx_accept(int sock, sockaddr *pAddr, socklen_t *pLen)
{
	return accept(sock, pAddr, pLen);
}

int socketx_connect(int sock, const sockaddr* pServAddr, socklen_t len, int iTimeOut)
{
	// 由于此socket已经被设置成了异步，因此它只是触发一个连接动作并没有等它真正的去连接
	if (0 != connect(sock, pServAddr, len))
	{
        // 此socket已经连接成功
		if (errno == EISCONN)
			return 0;
        
        // 在异步模式, 这些错误被认为是正确的
		if (errno != EINPROGRESS &&
            errno != EWOULDBLOCK &&
            errno != EAGAIN &&
            errno != EALREADY)
			return -1;
	}
    
	// 设置等待连接完成, 并设置超时
	fd_set fd;
	FD_ZERO(&fd);
	FD_SET(sock, &fd);
    
	struct timeval tv;
    tv.tv_sec  = iTimeOut / 1000;           // 取秒
    tv.tv_usec = (iTimeOut % 1000 ) * 1000; // 取微秒
    
    if (select(sock+1, NULL, &fd, NULL, &tv) <= 0) // 等待可写, 异常或者超时
		return -1;
    
    return 0;
}

int socketx_send(int sock, const void* pBuf, int iLen, int iFlags)
{
    return static_cast<int>(send(sock, pBuf, static_cast<int>(iLen), MSG_DONTWAIT));
}

int socketx_recv(int sock, void* pBuf, int iLen, int iFlags)
{
    // 设置等待接收数据, 并设置超时
    fd_set fd;
	FD_ZERO(&fd);
	FD_SET(sock, &fd);
    
	struct timeval tv;
    tv.tv_sec  = 3; // 取秒
    tv.tv_usec = 0; // 取微妙
    
    if (select(sock+1, &fd, NULL, NULL, &tv) <= 0) // 等待可读, 异常或者超时
		return -2;

    if (iFlags == 0)
        iFlags = MSG_DONTWAIT; // MSG_WAITSTREAM
    
	return static_cast<int>(recv(sock, pBuf, iLen, iFlags)); // 可能send过来了1000数据, 但是本次recv只拿到200. 不能使用MSG_WAITSTREAM, 可能会导致下一次recv出错
}
